# coding=utf-8
import json, requests, traceback, pexpect

import pika

from app.extensions import db, log
from flask import request
from functools import wraps
from datetime import date, datetime
from config import BaseConfig


class JsonEncoder:
    def __init__(self):
        pass


def recursive_json_loads(data):
    if isinstance(data, list):
        return [recursive_json_loads(i) for i in data]
    elif isinstance(data, tuple):
        return tuple([recursive_json_loads(i) for i in data])
    elif isinstance(data, dict):
        return Storage({recursive_json_loads(k): recursive_json_loads(data[k]) for k in data.keys()})
    else:
        try:
            obj = json.loads(data)
            if obj == data:
                return data
        except:
            return data
        return recursive_json_loads(obj)


class Storage(dict):
    """
    A Storage object is like a dictionary except `obj.foo` can be used
    in addition to `obj['foo']`.
        >>> o = storage(a=1)
        >>> o.a
        1
        >>> o['a']
        1
        >>> o.a = 2
        >>> o['a']
        2
        >>> del o.a
        >>> o.a
        Traceback (most recent call last):
            ...
        AttributeError: 'a'
    """

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError as k:
            raise AttributeError(k)

    def __setattr__(self, key, value):
        self[key] = value

    def __delattr__(self, key):
        try:
            del self[key]
        except KeyError as k:
            raise AttributeError(k)

    def __repr__(self):
        return '<Storage ' + dict.__repr__(self) + '>'


class ComplexEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(obj, date):
            return obj.strftime('%Y-%m-%d')
        else:
            return json.JSONEncoder.default(self, obj)


def DbCommit(cleanPro = False, cleanPlan = False, cleanCache = False):
    try:
        db.session.commit()
        log.info("============== db commit success ==============")
    except BaseException as e:
        _msg = e.message
        log.error("============== db commit error . %s ==============" % _msg)
        db.session.rollback()
        return {'success': False, 'msg': _msg}
    finally:
        if cleanPro:
            # 清除工程缓存
            from app.modules.cache.resources import deleteProCache
            deleteProCache()
        if cleanPlan:
            # 清除项目缓存
            from app.modules.cache.resources import deletePlanCache
            deletePlanCache()
        if cleanCache:
            # 清除项目缓存
            from app.modules.cache.resources import cacheClear
            cacheClear()

    return {'success': True, 'msg': ""}


def checkToken(func):
    @wraps(func)
    def check(*args):
        try:
            printArgs(request)
            # 1.在白名单内的链接不需要校验token_id
            _urls = getConfigJson("url_with_out_auth")
            if str(request.url_rule) in _urls:
                return func(*args, login_user_id = None, login_user_name = None)
            # 2.其它工程需要校验
            _token_id = request.values['token_id']
            if _token_id is None:
                _msg = "tokenid is empty"
                log.error("============== %s %s ==============" % (request.url_rule, _msg))
                return {'success': False, 'msg': _msg}
            _url = '%s?token_id=%s' % (BaseConfig.BEETLE_AUTH_URL, _token_id)
            _obj = recursive_json_loads(requests.get(url = _url).content)
            if _obj['success']:
                _data = _obj['data']
                log.info("============== %s : username is %s ==============" % (request.url_rule, _data['account']))
                _role = None
                for ro in _data['role_list']:
                    if ro.role_name in ['dev', 'qa', 'ops']:
                        _role = ro.role_name
                        break
                if _role is None:
                    for ro in _data['role_list']:
                        if ro.role_name == 'admin':
                            _role = ro.role_name
                            break
                # login_user_name  当用户的realname存在时,使用realname,否则使用account
                if _data['realname'] is None:
                    return func(*args, login_user_id = _data['id'], login_user_name = _data['account'],
                                login_user_account = _data['account'], login_user_realname = None,
                                login_user_role = _role, login_user_zt_id = _data['zt_user_id'],
                                login_user_zt_account = _data['zt_user_account'])
                else:
                    return func(*args, login_user_id = _data['id'], login_user_name = _data['realname'],
                                login_user_account = _data['account'], login_user_realname = _data['realname'],
                                login_user_role = _role, login_user_zt_id = _data['zt_user_id'],
                                login_user_zt_account = _data['zt_user_account'])
            else:
                _msg = _obj['msg']
                log.error("============== %s %s ==============" % (request.url_rule, _msg))
                return {'success': False, 'msg': _msg}
        except BaseException:
            _msg = "get error %s" % traceback.format_exc()
            log.error("============== %s %s ==============" % (request.url_rule, _msg))
            return {'success': False, 'msg': _msg}

    return check


def printArgs(request):
    _token_id = None
    args = request.values
    if 'token_id' in args:
        _token_id = args['token_id']
    for key in args:
        if key != 'token_id':
            log.info("============== %s : token_id is %s . %s is %s ==============" % (
                request.url_rule, _token_id, key, args[key]))
        else:
            log.info("============== %s : token_id is %s ==============" % (request.url_rule, _token_id))


def strToList(string):
    _s = str(string).replace('null', 'None').replace('true', 'True').replace('false', 'False')
    return eval(_s)


def getConfigJson(key):
    from jsonFileUtil import getJsonFile
    _json = getJsonFile("%s/data/config.json" % BaseConfig.PROJECT_ROOT)
    return _json[key]


def runCommand(command, withexitstatus = 1, timeout = 600):
    output, status = pexpect.run(command, withexitstatus = withexitstatus, timeout = timeout)
    log.info("============== run command : %s ==============" % command)
    if status == 0:
        log.info("============== run command success : %s . %s ==============" % (command, output))
        return {'success': True, 'msg': output}
    else:
        log.error("============== run command error : %s . %s ==============" % (command, output))
        return {'success': False, 'msg': output}


# 发送mq消息
# message 格式 {"type":"ONLINE","map":{"key":'aaaa',"value":2}} type目前可填ONLINE/PROJECT
def sendMqMessage(exchange, message):
    log.info("============== sendMqMessage message is %s ==============" % message)
    # matrix项目停止，不再发送消息
    # connection = pika.BlockingConnection(
    #     pika.URLParameters(url = BaseConfig.MQ_URL))
    # channel = connection.channel()
    # channel.basic_publish(exchange = exchange,
    #                       routing_key = 'no-use',
    #                       body = json.dumps(message),
    #                       properties = pika.BasicProperties(
    #                           content_type = "application/json",
    #                           headers = {'__TypeId__': 'com.winit.common.CloudLinkSend.MqMessage'},
    #                           content_encoding = 'UTF-8',
    #                           delivery_mode = 2,  # make message persistent
    #                       ))
    # connection.close()
